/*
  SparkFun Electronics 2010
  Playing with IR remote control
  
  IR Receiver TSOP382: Supply voltage of 2.5V to 5.5V
  With the curved front facing you, pin 1 is on the left.
  Attach
    Pin 1: To pin 2 on Arduino
    Pin 2: GND
    Pin 3: 5V
  
  This is based on pmalmsten's code found on the Arduino forum from 2007:
  http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1176098434/0

  This code works with super cheapo remotes. If you want to look at the individual timing
  of the bits, use this code:
  http://www.arduino.cc/playground/Code/InfraredReceivers
  
  This code clips a lot of the incoming IR blips, but what is left is identifiable as key codes.
Changed melody3 so that second beat is shorter.
733am attempting to
*/

int currentMenu=0;
int currentState=1;

const int POWERED_OFF = 0;
const int POWERED_ON = 1;
const int MUTE = 3;

const int POWER_MENU = 0;
const int MAIN_MENU = 10;
const int DRIVE_MENU = 20;
const int DISTANCE_MENU = 30;
const int SOUND_MENU = 40;

int irPin = 2; //Sensor pin 1 wired to Arduino's pin 2
int statLED = 13; //Toggle the status LED every time Power is pressed
int start_bit = 2200; //Start bit threshold (Microseconds)
int bin_1 = 1000; //Binary 1 threshold (Microseconds)
int bin_0 = 400; //Binary 0 threshold (Microseconds)
int key=0;

//String mainMenuOptionsText[]  = {"1 Drive", "2 Distance Level","3 Sound"};
String mainMenuOptionsText[]  = {"1 Drive", "","2 Distance Level","", "3 Sound",""};
int mainMenuOptions[] = {DRIVE_MENU,0, DISTANCE_MENU,0, SOUND_MENU,0};
/*
 Integrating Distance Reader with Distance Displayer
 Distance Reader = Maxbotix Ultrasensor
 Distance Displayer = 16x2 LCD
*/

// using the maxsonar quick start http://www.adafruit.com
// http://www.adafruit.com/index.php?main_page=product_info&cPath=35&products_id=172

#include <LiquidCrystal.h>
// Initialize the library with the numbers of the interface pins 
LiquidCrystal lcd(12, 11, 5, 4, 3, 7); // Create an lcd object and assign the pins
//LiquidCrystal lcd(12, 11, 5, 4, 3, 2); // Create an lcd object and assign the pins

//DISTANCE SENSOR
int sonarPin = 0; //pin connected to analog out on maxsonar sensor
int inchesAway;   // distance
int timeDelay = 500; // delay
int alert;

const int SAFETY_LEVEL_1 = 1;
const int SAFETY_LEVEL_2 = 2;
const int SAFETY_LEVEL_3 = 3;

int levelOneDistanceLimit = 15;
int levelTwoDistanceLimit = 25;

//SOUND
unsigned long echo = 0;
int ultraSoundSignal = 9;
unsigned long ultrasoundValue = 0;
boolean WELCOME = true;
int speakerOut = 8;
// Do we want debugging on serial out? 1 for yes, 0 for no
int DEBUG = 0;

// MELODY and TIMING  =======================================
//  melody[] is an array of notes, accompanied by beats[], 
//  which sets each note's relative length (higher #, longer note) 
//int melody[] = {  C,  b,  g,  C,  b,   e,  R,  C,  c,  g, a, C };
//int melody[] = {  956,  1014,  1275,  956,  1014,   1519,  0,  956,  1915,  1275, 1136, 956 };
//int beats[]  = { 16, 16, 16,  8,  8,  16, 32, 16, 16, 16, 8, 8 }; 
int melody[] = { 956,  1915};//,  1275, 1136, 956, 0}; //,  956,  1915,  1275, 1136, 956 };
int beats[]  = { 16,   16};//,    16,   8,    8 , 32};//, 16, 16, 16, 8, 8 }; 
int melody3[] = {  956,  0};//, 1275, 0,  956, 0, 1275, 0, 956, 0, 1275, 0   };
int beats3[]  = { 16, 16};//, 16, 8,  16,  8,  16, 8, 16, 8, 16, 8 }; 
int melody2[] = {  1275,  956};//, 1275,  956, 1275,   956,  0};//,   1275,  956, 1275, 956,  1275,0 };
int beats2[]  = { 4,      4};//,   4,     4,   4,      4,    4}; //, 4, 4, 4, 4, 4, 16 };

int melody1[] = {  1275,  956};//, 1275,  956, 1275,   956,  0};//,   1275,  956, 1275, 956,  1275,0 };
int beats1[]  = {  2,     2};//,   2,     2,   2,      2,    4};//, 2, 2, 2, 2, 2, 16 }; 

int MAX_COUNT = sizeof(melody) / 2; // Melody length, for looping.

// Set overall tempo
//long tempo = 10000;
long tempo = 50000;
// Set length of pause between notes
int pause = 1000;
int pause2 = 100;
// Loop variable to increase Rest length
int rest_count = 100; //<-BLETCHEROUS HACK; See NOTES

// Initialize core variables
int tone_ = 0;
int beat = 0;
long duration  = 0;


void setup() {
  Serial.begin(9600);
  pinMode(speakerOut, OUTPUT);
  if (DEBUG) { 
    Serial.begin(9600); // Set serial out if we want debugging
  } 
  lcd.begin(16, 2); // Set the display to 16 columns and 2 rows
  
  //IR Key
  pinMode(statLED, OUTPUT);
  digitalWrite(statLED, LOW);

  pinMode(irPin, INPUT);
  key=0;
  currentState=POWERED_OFF;
}

void loop() {
  if (currentState==POWERED_OFF) {
    key = getIRKey();  //Fetch the remote key input.
    if (userSelectsSystemPowerOn(key)) { 
      currentState = POWERED_ON;
      //key = 0;
      WELCOME = true;
      Serial.println("chose power on button");
    }
  } 
    if (currentState != POWERED_OFF) {
      Serial.println("powered on"); 
  inchesAway = analogRead(sonarPin) /2; // read analog
  ////setCursorDemo(inchesAway);
  //delay(timeDelay); // delay
  Serial.println(inchesAway); // print value
  if (WELCOME) {
      displayBasicOnLCD("Initialising...");
      scrollLeftOnLCD("", "Driving Assistant");
      playWelcomeMelody();
      WELCOME = false;
      //key = getIRKey();
      //displayMenuOnLCD("(1) Drive",false, true );
      //displayMenuOnLCD(MainMenuOptionsText,false, true );
      ////displayBasicOnLCD("Main Menu");
      ////displayMenuOnLCD(mainMenuOptionsText, mainMenuOptions, 3);
      currentMenu = MAIN_MENU;
      
      //displayDistanceRanges();  ///GVTest
    }
    
    switch(currentMenu) {
       case MAIN_MENU:
        displayBasicOnLCD("Main Menu");
        displayMenuOnLCD(mainMenuOptionsText, mainMenuOptions, 6);
        break;
       case DISTANCE_MENU:
         Serial.println("distance menu");
         displayDistanceRanges();  ///GVTest
         currentMenu = MAIN_MENU;
         break;
         
       case SOUND_MENU:
         Serial.println("sound menu");
         displaySoundMenu();
         currentMenu = MAIN_MENU;
         break;
         
       case DRIVE_MENU:
         Serial.println("drive menu");
       
       default:
         Serial.println("default menu");
          displayDistanceOnLCD(inchesAway);
    
          alert = getSafetyLevel();        
          if (alert == 3) {
            Serial.println("Playing alert 3");
            playLevelThreeAlert();
          }

          alert = getSafetyLevel(); //getAlertLevel(x);
          if (alert==2) {
            playLevelTwoAlert(); 
            Serial.println("Playing alert 2");      
          }      
  
          if (alert==1) { 
           playLevelOneAlert(); 
           Serial.println("Playing alert 1"); 
          }
          break;
       
    }
    } 
}



// PLAY TONE  ==============================================
// Pulse the speaker to play a tone for a particular duration
void playTone() {
  long elapsed_time = 0;
  if (tone_ > 0) { // if this isn't a Rest beat, while the tone has 
    //  played less long than 'duration', pulse speaker HIGH and LOW
    while (elapsed_time < duration) {

      digitalWrite(speakerOut,HIGH);
      delayMicroseconds(tone_ / 2);

      // DOWN
      digitalWrite(speakerOut, LOW);
      delayMicroseconds(tone_ / 2);

      // Keep track of how long we pulsed
      elapsed_time += (tone_);
    } 
  }
  else { // Rest beat; loop times delay
    for (int j = 0; j < rest_count; j++) { // See NOTE on rest_count
      delayMicroseconds(duration/2);  
    }                                
  }                                 
}

int getSafetyLevel() {
  int safetyLevel = SAFETY_LEVEL_3; //3;
  inchesAway = analogRead(sonarPin) /2;
  Serial.println(inchesAway);

  if (inchesAway <= levelOneDistanceLimit && WELCOME == false ) { // if something is 24 inches away then make a 1khz sound
    //safetyLevel = 1;
    safetyLevel = SAFETY_LEVEL_1;   
  } else if (inchesAway <= levelOneDistanceLimit) {
    //safetyLevel = 2; 
    safetyLevel = SAFETY_LEVEL_2;  
  } 
  return safetyLevel;
}

void playWelcomeMelody() {
  // Welcome Melody
        for (int i=0; i<MAX_COUNT; i++) {
          tone_ = melody[i];
          beat = beats[i];

          duration = beat * tempo; // Set up timing

          playTone(); 
          // A pause between notes...
          //delayMicroseconds(pause);

          if (DEBUG) { // If debugging, report loop, tone, beat, and duration
            Serial.print(i);
            Serial.print(":");
            Serial.print(beat);
            Serial.print(" ");    
            Serial.print(tone_);
            Serial.print(" ");
            Serial.println(duration);
          }
        }
}
void playLevelThreeAlert() {
  
//if (alert == 3) {
         //Safe sound
        // Set up a counter to pull from melody[] and beats[]
        for (int i=0; i<MAX_COUNT; i++) {
          tone_ = melody3[i];
          beat = beats3[i];

          duration = beat * tempo; // Set up timing

          playTone(); 
          // A pause between notes...
          // delayMicroseconds(pause);

          if (DEBUG) { // If debugging, report loop, tone, beat, and duration
              Serial.print(i);
              Serial.print(":");
              Serial.print(beat);
              Serial.print(" ");    
              Serial.print(tone_);
              Serial.print(" ");
              Serial.println(duration);
          }
        }
  //  }

}

void playLevelTwoAlert() {
  // Set up a counter to pull from melody[] and beats[]
        for (int i=0; i<MAX_COUNT; i++) {
              tone_ = melody2[i];
              beat = beats2[i];

              duration = beat * tempo; // Set up timing

              playTone(); 
              // A pause between notes...
              // delayMicroseconds(pause2);

             if (DEBUG) { // If debugging, report loop, tone, beat, and duration
                  Serial.print(i);
                  Serial.print(":");
                  Serial.print(beat);
                  Serial.print(" ");    
                  Serial.print(tone_);
                  Serial.print(" ");
                  Serial.println(duration);
             }
         }
}

void playLevelOneAlert() {
  //Unsafe
  // Set up a counter to pull from melody[] and beats[]
  for (int i=0; i<MAX_COUNT; i++) {
    tone_ = melody1[i];
    beat = beats1[i];

    duration = beat * tempo; // Set up timing

    playTone(); 
    // A pause between notes...
    //delayMicroseconds(pause2);

    if (DEBUG) { // If debugging, report loop, tone, beat, and duration
      Serial.print(i);
      Serial.print(":");
      Serial.print(beat);
      Serial.print(" ");    
      Serial.print(tone_);
      Serial.print(" ");
      Serial.println(duration);
    }
  }
}

void scrollLeftOnLCD(String textToDisplay1, String textToDisplay2) { 
	lcd.clear(); // Clear the display 
	lcd.setCursor(7,0); 
	lcd.print(textToDisplay1); 
	lcd.setCursor(9,1); 
	lcd.print(textToDisplay2); 
	delay(1000); 
	for(int x=0; x<16; x++) {
	  lcd.scrollDisplayLeft(); // Scroll display left 16 times 
	  delay(250);
	}
}

void displayBasicOnLCD(String textToDisplay) { 
	lcd.clear(); // Clear the display 
        lcd.print(textToDisplay);
	delay(2000);
}

int getIRKey() {
  int data[12];
  int i;

  while(pulseIn(irPin, LOW) < start_bit); //Wait for a start bit
  
  for(i = 0 ; i < 11 ; i++)
    data[i] = pulseIn(irPin, LOW); //Start measuring bits, I only want low pulses
  
  for(i = 0 ; i < 11 ; i++) //Parse them
  {	    
    if(data[i] > bin_1) //is it a 1?
      data[i] = 1;
    else if(data[i] > bin_0) //is it a 0?
      data[i] = 0;
    else
      return -1; //Flag the data as invalid; I don't know what it is! Return -1 on invalid data
  }

  int result = 0;
  for(i = 0 ; i < 11 ; i++) //Convert data bits to integer
    if(data[i] == 1) result |= (1<<i);  

  return result; //Return key number
} 

void displayDistanceOnLCD(int distance) { 
	lcd.clear(); // Clear the display 
	lcd.print("Distance"); // Print some text 
	lcd.setCursor(1,0); // Cursor at column 5 row 0  
	lcd.setCursor(10,1); // Cursor at column 10 row 1 
        lcd.print(distance);// +" inches"); 

        lcd.setCursor(13,1); // Cursor at column 10 row 1 
        lcd.print("in");// +" inches");  
}

/*
void displayMenuOnLCD(String message[], boolean hasPrevious, boolean hasNext) { 
	lcd.clear(); // Clear the display 
        key = 0;
        int currentMenuIndex = 0;
        while (key == 0) {
	  lcd.setCursor(1,0); // Cursor at column 5 row 0 
          lcd.print(message[currentMenuIndex]);// +" inches"); 
	
          if (hasPrevious) {
            lcd.setCursor(1,1); // Cursor at column 10 row 1  
            lcd.print("< PREV");// +" inches
          }
        
          if (hasNext) {
            lcd.setCursor(12,1); // Cursor at column 10 row 1  
            lcd.print("NEXT >");// +" inches
          }
          delay(3000);
          key = getIRKey();
          switch(key) {
            case 144: Serial.print("CH Up"); break;
            case 145: Serial.print("CH Down"); break;
            case 146: 
              Serial.print("VOL Right");
              if ((currentMenuIndex + 1) < sizeof(message)) { 
                currentMenuIndex++;
              }
              key=0;
              break;
            case 147: 
              Serial.print("VOL Left"); 
              if ((currentMenuIndex - 1) >= 0) { 
                currentMenuIndex++;
              }
              key=0;
              break;
            case 148: Serial.print("Mute"); break;
            case 165: 
              Serial.print("AV/TV"); 
              //We define this as the OK button for this remote.
              switch(currentMenuIndex) {
                case 0:
                case 1
              }
              break;
            case 149: 
              Serial.print("Power");  
              break;
            default: Serial.print(key);
    }
       }
        
}
*/

void displayMenuOnLCD(String message[], int options[], int numOptions) { 
	//lcd.clear(); // Clear the display 
        key = 0;
        int currentMenuIndex = 0;
        boolean nextMenu=false;
        boolean optionSelected = false;
        //while (key == 0) {
        while (!optionSelected) {
          lcd.clear(); // Clear the display 
	  lcd.setCursor(1,0); // Cursor at column 5 row 0 
          lcd.print(message[currentMenuIndex]);// +" inches"); 
	  //delay(300);
          if (currentMenuIndex > 0) {
            lcd.setCursor(1,1); // Cursor at column 10 row 1  
            lcd.print("< PREV");// +" inches
          }
        
          Serial.print("size:");
          Serial.println(sizeof(message));
          //if (currentMenuIndex < (sizeof(message)-1)) {
          if (currentMenuIndex < (numOptions-1)) {
            lcd.setCursor(10,1); // Cursor at column 10 row 1  
            lcd.print("NEXT >");// +" inches
          }
          //delay(300);
          //if (!nextMenu) {
            key = getIRKey();
            //delay(300);
            nextMenu = false;
          //}
          switch(key) {
            case 144: Serial.print("CH Up"); break;
            case 145: Serial.print("CH Down"); break;
            case 146: 
              Serial.print("VOL Right");
              //if ((currentMenuIndex + 1) < sizeof(message)) { 
              if ((currentMenuIndex) < (numOptions-1)) { 
                currentMenuIndex++;
              }
              key=0;
              
              //nextMenu = !nextMenu;
              break;
            case 147: 
              Serial.print("VOL Left"); 
              if ((currentMenuIndex - 1) >= 0) { 
                currentMenuIndex--;
              }
              key=0;
              //nextMenu = !nextMenu;
              break;
            case 148: Serial.print("Mute"); break;
            case 165: 
              Serial.print("AV/TV"); 
              //We define this as the OK button for this remote.
              currentMenu = options[currentMenuIndex];
              optionSelected = true;
              break;
            case 149: 
              Serial.print("Power");
              optionSelected = true;  
              break;
            default: Serial.print(key);
              key = getIRKey();
        }
       }
        
}

void displaySoundMenu() {
  displayBasicOnLCD("Playing Sounds");
  //scrollLeftOnLCD("","Alert 3: Very Safe");
  displayTwoLinesOnLCD("Alert 3", "Very Safe");
  //displayBasicOnLCD("Alert 3: Very Safe");
  playLevelThreeAlert(); 
  playLevelThreeAlert(); 
  //scrollLeftOnLCD("","Alert 2: Medium Safe");
  displayTwoLinesOnLCD("Alert 2", "Medium Safe");
  //displayBasicOnLCD("Alert 2: Medium Safe");
  playLevelTwoAlert();
  playLevelTwoAlert(); 
  playLevelTwoAlert();
  delay(1000);
  //scrollLeftOnLCD("","Alert 1: High Alert");
  displayTwoLinesOnLCD("Alert 1", "Very UNSafe");
  //displayBasicOnLCD("Alert 1: High Alert");
  playLevelOneAlert(); 
  playLevelOneAlert(); 
  playLevelOneAlert();
  displayBasicOnLCD("Returning to..");
}

void displayTwoLinesOnLCD(String message1, String message2) {
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print(message1);
  lcd.setCursor(0,1);
  lcd.print(message2);
  key = getIRKey();
  
  delay(1000);
}

void displayTwoNumbersOnLCD(int message1, int message2) {
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("From");
  lcd.setCursor(5,0);
  lcd.print(message1);
  lcd.setCursor(0,1);
  lcd.print("to");
  lcd.setCursor(5,1);
  lcd.print(message2);
  lcd.setCursor(10,1); // Cursor at column 10 row 1  
            lcd.print("NEXT >");// +" inches
  key = getIRKey();
  delay(1000);
}

void displayDistanceRanges(){
  displayTwoLinesOnLCD("Distance ranges","defined are:");
  key = getIRKey();
  displayTwoLinesOnLCD("Level 1:","distance range:");
  key = getIRKey();
  displayTwoNumbersOnLCD(0,levelOneDistanceLimit);
  key = getIRKey();
  displayTwoLinesOnLCD("Level 2:","distance range:");
  key = getIRKey();
  displayTwoNumbersOnLCD(levelOneDistanceLimit+1,levelTwoDistanceLimit);
  key = getIRKey();
  displayTwoLinesOnLCD("Level 3:","distance range:");
  key = getIRKey();
  displayTwoNumbersOnLCD(levelTwoDistanceLimit+1,300);
  key = getIRKey();
}

boolean userSelectsSystemPowerOn(int keySelected) {
  Serial.print("Key Recieved: "+ keySelected);
  //int menuOption = 0;
  boolean mustTurnOnPower = false;
    switch(keySelected)
    {
      case 149: 
        Serial.print("Power!!");
        mustTurnOnPower = true;
        //currentMenu = MAIN_MENU;
        break;
      default: 
        Serial.print(key);
    }
        
    return mustTurnOnPower;
}
